package gameoflife;

import java.util.List;

public class GameOfLife {

	private static final int NUMBER_OF_LIVE_NEIGHBOURS_TO_BORN = 3;
	private static final int MAX_NUMBER_OF_LIVE_NEIGHBOURS_TO_LIVE = 3;
	private static final int MIN_NUMBER_OF_LIVE_NEIGHBOURS_TO_LIVE = 2;
	private Grid grid;

	public GameOfLife(int rows, int columns) {
		grid = new Grid(rows, columns);
		makeAllCellsDeath(grid);
	}

	private void makeAllCellsDeath(Grid grid) {
		for (int x = 0; x < grid.rows(); x++)
			for (int y = 0; y < grid.columns(); y++)
				grid.addCells(GameOfLifeCell.death(x, y));
	}

	public void tick() {
		Grid newGrid = new Grid(grid.rows(), grid.columns());
		for (int x = 0; x < grid.rows(); x++)
			for (int y = 0; y < grid.columns(); y++) {
				GameOfLifeCell cell = (GameOfLifeCell) grid.getCellAt(x, y);
				newGrid.addCells(getForNextGeneration(cell));
			}
		this.grid = newGrid;
	}

	private Cell getForNextGeneration(GameOfLifeCell cell) {
		if (cell.isLive() && shouldLiveInNextGeneration(cell))
			return GameOfLifeCell.live(cell.x(), cell.y());
		if (!cell.isLive() && shouldBornInNextGeneration(cell))
			return GameOfLifeCell.live(cell.x(), cell.y());
		return GameOfLifeCell.death(cell.x(), cell.y());
	}

	private boolean shouldLiveInNextGeneration(Cell cell) {
		List<Cell> neighbours = grid.getNeighboursForCell(cell);
		int numberOfLiveNeighbours = getNumberOfLive(neighbours);
		return numberOfLiveNeighbours >= MIN_NUMBER_OF_LIVE_NEIGHBOURS_TO_LIVE
				&& numberOfLiveNeighbours <= MAX_NUMBER_OF_LIVE_NEIGHBOURS_TO_LIVE;
	}

	private boolean shouldBornInNextGeneration(GameOfLifeCell cell) {
		List<Cell> neighbours = grid.getNeighboursForCell(cell);
		int numberOfLiveNeighbours = getNumberOfLive(neighbours);
		return numberOfLiveNeighbours == NUMBER_OF_LIVE_NEIGHBOURS_TO_BORN;
	}

	public boolean isLive(int x, int y) {
		GameOfLifeCell cell = (GameOfLifeCell) grid.getCellAt(x, y);
		return cell.isLive();
	}

	private int getNumberOfLive(List<Cell> neighbours) {
		int result = 0;
		for (Cell neighbour : neighbours) {
			GameOfLifeCell cell = (GameOfLifeCell) neighbour;
			if (cell.isLive())
				result++;
		}
		return result;
	}

	public void setLive(int x, int y) {
		grid.addCells(GameOfLifeCell.live(x, y));
	}

	public int getRows() {
		return grid.rows();
	}

	public int getColumns() {
		return grid.columns();
	}

	@Override
	public String toString() {
		StringBuilder result = new StringBuilder();
		for (int x = 0; x < grid.rows(); x++) {
			for (int y = 0; y < grid.columns(); y++) {
				GameOfLifeCell cell = (GameOfLifeCell) grid.getCellAt(x, y);
				if (cell.isLive())
					result.append("*");
				else
					result.append("-");
			}
			result.append("\n");
		}
		return result.toString();
	}

}
